/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../helper_objs/head.h"
#include "../msg_aio/head.h"
#include "../config/head.h"
#include "../impl/head.h"
#include "../macro/head.h"
#include "../msg/head.h"
#include "connection.h"

namespace xos_net
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static xos_box::i_all_done_test * all_done_test_ptr = 0;
    static connection::ITEM_DATA xos_item_data;
    static bool b_done_server = true;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    connection::connection()
    {
        init_data();
    }

    connection::~connection()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::get_xos_item_data( ITEM_DATA *& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int connection::static_user_init()
    {
        int ret = 0;
        all_done_test_ptr = mgr::container()->box()->all_done_test();
        return ret;
    }

    int connection::static_user_term()
    {
        int ret = 0;
        xos_stl::release_interface( all_done_test_ptr );
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    xos_box::i_all_done_test * connection::done_test()
    {
        return all_done_test_ptr;
    }

    bool connection::done_test_server()
    {
        return b_done_server;
    }

    int connection::stop_all_server()
    {
        int ret = 0;

        done_test()->set_notify( helper_main::get(), MAIN_ALL_SERVER_STOPPED );
        done_test()->reset();
        b_done_server = true;

        connection::LIST & con_list = *connection::list();

        for( connection::ITER iter = con_list.begin(); iter != con_list.end(); ++iter )
        {
            connection::T * pT = *iter;
            if( pT->tcp_data() )
            {
                continue;
            }
            xos_box::i_msg * pMsg = 0;
            // 没有真正执行关闭动作时，post_close函数内部会减掉这个计数
            // 真正关闭时，会在xos_http中减掉这个计数
            pT->add_ref();
            int r = pT->post_close( pMsg, false );
            if( 0 == r )
            {
                done_test()->add_num( 1 );
            }
        }

        done_test()->test( 0 );

        return ret;
    }

    int connection::stop_all_connect()
    {
        int ret = 0;

        done_test()->set_notify( helper_main::get(), MAIN_ALL_CONNECT_STOPPED );
        done_test()->reset();
        b_done_server = false;

        connection::LIST & con_list = *connection::list();

        for( connection::ITER iter = con_list.begin(); iter != con_list.end(); ++iter )
        {
            connection::T * pT = *iter;
            xos_box::i_msg * pMsg = 0;
            // 没有真正执行关闭动作时，post_close函数内部会减掉这个计数
            // 真正关闭时，会在xos_http中减掉这个计数
            pT->add_ref();
            int r = pT->post_close( pMsg, false );
            if( 0 == r )
            {
                done_test()->add_num( 1 );
            }
        }

        done_test()->test( 0 );

        return ret;
    }

    int connection::heart()
    {
        int ret = 0;

        connection::LIST * pList = list();

        for( connection::ITER iter = pList->begin(); iter != pList->end(); iter++ )
        {
            connection * pT = *iter;
            if( pT->tcp_data() && pT->running() )
            {
                check_timeout( pT );
            }
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::check_timeout( connection * pCon )
    {
        int ret = 0;

        tcp * pTcp = pCon->m_pTcp;

        pCon->net_timeout_ms -= config::get()->heart_check_interval_ms;

        if( pCon->net_timeout_ms <= 0 )
        {
            LOG4( "(%s,%d) time left = %d s, connect reset", pTcp->m_peer_ip.c_str(), pTcp->m_nPeerPort, pCon->net_timeout_ms / 1000 );
            // 没有真正执行关闭动作时，post_close函数内部会减掉这个计数
            // 真正关闭时，会在xos_http中减掉这个计数
            xos_box::i_msg * pMsg = 0;
            pCon->add_ref();
            pCon->post_reset( pMsg, false );
        }
        else
        {
            LOG4( "(%s,%d) time left = %d s", pTcp->m_peer_ip.c_str(), pTcp->m_nPeerPort, pCon->net_timeout_ms / 1000 );
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::post_init( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_init( pMsg, this );
        }
        else
        {
            aio_udp obj;
            ret = obj.post_init( pMsg, this );
        }

        return ret;
    }

    int connection::post_connect( xos_box::i_msg *& pMsg )
    {
        int ret = 0;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_connect( pMsg, this );
        }

        return ret;
    }

    int connection::post_send( xos_box::i_msg *& pMsg, const char * lpszIp, int nPort, xos_box::i_buf *& pBuf, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        connection * pCon = this;

        if( m_pUdp )
        {
            aio_udp obj;
            ret = obj.post_send( pMsg, pCon, pBuf, lpszIp, nPort, bCloseAfterSend, bQuitAfterSend );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    int connection::post_send( xos_box::i_msg *& pMsg, xos_box::i_buf *& pBuf, bool bCloseAfterSend, bool bQuitAfterSend )
    {
        int ret = 0;

        connection * pCon = this;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_send( pMsg, pCon, pBuf, bCloseAfterSend, bQuitAfterSend );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    int connection::post_shutdown( xos_box::i_msg *& pMsg, int how )
    {
        int ret = 0;

        //int how = xos::i_socket::XOS_SD_BOTH;
        m_nReadWritePermmit = how;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_shutdown( pMsg, this, how );
        }
        else
        {
            aio_udp obj;
            ret = obj.post_shutdown( pMsg, this, how );
        }

        return ret;
    }

    int connection::post_close( xos_box::i_msg *& pMsg, bool bNotifyIfAlreadyClosed )
    {
        int ret = 0;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_close( pMsg, this, bNotifyIfAlreadyClosed );
        }
        else
        {
            aio_udp obj;
            ret = obj.post_close( pMsg, this, bNotifyIfAlreadyClosed );
        }

        return ret;
    }

    int connection::post_reset( xos_box::i_msg *& pMsg, bool bNotifyIfAlreadyClosed )
    {
        int ret = 0;

        if( m_pTcp )
        {
            aio_tcp obj;
            ret = obj.post_reset( pMsg, this, bNotifyIfAlreadyClosed );
        }
        else
        {
            aio_udp obj;
            ret = obj.post_close( pMsg, this, bNotifyIfAlreadyClosed );
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::timeout_check()
    {
        int ret = 0;
        return ret;
    }

    int connection::init_data()
    {
        int ret = 0;

        m_nReadWritePermmit = xos::i_socket::XOS_SD_BOTH;
        net_timeout_ms = 0;
        m_pRefNum = 0;

        m_eState = enumState::STATE_NONE;
        m_eType = enumType::TYPE_NONE;

        m_connect_iter = list()->end();

        m_bReleaseAfterClose = true;
        m_pListenCon = 0;
        m_pTcp = 0;
        m_pUdp = 0;

        // data consumer
        m_pUserData = 0;

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int connection::set_timeout_time_s( int nSecond )
    {
        int ret = 0;
        net_timeout_ms = nSecond * 1000;
        return ret;
    }

    xos_box::i_notify * connection::notify() const
    {
        return m_pUserData->connect_notify();
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    xos_aio::i_connect * connection::aio_connect()
    {
        return this;
    }

    net::i_connect * connection::net_connect()
    {
        return this;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int connection::init()
    {
        int ret = 0;

        {
            m_pRefNum = mgr::container()->box()->lnum();
            m_pRefNum->set( 1 );
        }

        return ret;
    }

    int connection::term()
    {
        int ret = 0;

        if( m_connect_iter != list()->end() )
        {
            list()->erase( m_connect_iter );
            m_connect_iter = list()->end();
        }

        xos_stl::release_interface( m_pRefNum );
        xos_stl::release_interface( m_pTcp );
        xos_stl::release_interface( m_pUdp );

        init_data();

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::set_tcp_listen()
    {
        int ret = 0;
        m_eType = enumType::TYPE_TCP_LISTEN;
        return ret;
    }

    int connection::set_tcp_data()
    {
        int ret = 0;
        m_eType = enumType::TYPE_TCP_DATA;
        return ret;
    }

    int connection::set_udp()
    {
        int ret = 0;
        m_eType = enumType::TYPE_UDP;
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    bool connection::tcp_listen() const
    {
        return enumType::TYPE_TCP_LISTEN == m_eType;
    }

    bool connection::tcp_data() const
    {
        return enumType::TYPE_TCP_DATA == m_eType;
    }

    bool connection::is_udp() const
    {
        return enumType::TYPE_UDP == m_eType;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::set_none()
    {
        int ret = 0;
        m_eState = enumState::STATE_NONE;
        return ret;
    }

    int connection::set_connect_failed()
    {
        int ret = 0;
        m_eState = enumState::STATE_CONNECT_FAILED;
        return ret;
    }

    int connection::set_connecting()
    {
        int ret = 0;
        m_eState = enumState::STATE_CONNECTING;
        return ret;
    }

    int connection::set_init_failed()
    {
        int ret = 0;
        m_eState = enumState::STATE_INIT_FAILED;
        return ret;
    }

    int connection::set_initing()
    {
        int ret = 0;
        m_eState = enumState::STATE_INITING;
        return ret;
    }

    int connection::set_running()
    {
        int ret = 0;
        m_eState = enumState::STATE_RUNNING;
        return ret;
    }

    int connection::set_need_close()
    {
        int ret = 0;
        m_eState = enumState::STATE_NEED_CLOSE;
        return ret;
    }

    int connection::set_timeout()
    {
        int ret = 0;
        m_eState = enumState::STATE_TIME_OUT;
        return ret;
    }

    int connection::set_closing()
    {
        int ret = 0;
        m_eState = enumState::STATE_CLOSING;
        return ret;
    }

    int connection::set_closed()
    {
        int ret = 0;
        m_eState = enumState::STATE_CLOSED;
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    bool connection::none() const
    {
        return enumState::STATE_NONE == m_eState;
    }

    bool connection::connect_failed() const
    {
        return enumState::STATE_CONNECT_FAILED == m_eState;
    }

    bool connection::connecting() const
    {
        return enumState::STATE_CONNECTING == m_eState;
    }

    bool connection::init_failed() const
    {
        return enumState::STATE_INIT_FAILED == m_eState;
    }

    bool connection::initing() const
    {
        return enumState::STATE_INITING == m_eState;
    }

    bool connection::running() const
    {
        return enumState::STATE_RUNNING == m_eState;
    }

    bool connection::need_close() const
    {
        return enumState::STATE_NEED_CLOSE == m_eState;
    }

    bool connection::timeout() const
    {
        return enumState::STATE_TIME_OUT == m_eState;
    }

    bool connection::closing() const
    {
        return enumState::STATE_CLOSING == m_eState;
    }

    bool connection::closed() const
    {
        return enumState::STATE_CLOSED == m_eState;
    }

    bool connection::can_read() const
    {
        int nState = xos::i_socket::XOS_SD_RECEIVE;
        bool bRet = ( ( m_nReadWritePermmit & nState ) == nState );
        return bRet;
    }

    bool connection::can_send() const
    {
        int nState = xos::i_socket::XOS_SD_SEND;
        bool bRet = ( ( m_nReadWritePermmit & nState ) == nState );
        return bRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int connection::set_user_data( void * pUserData )
    {
        int ret = 0;
        m_pUserData = (xos_net::i_connect *)pUserData;
        return ret;
    }

    void * connection::get_user_data()
    {
        return m_pUserData;
    }

    int connection::release_ref()
    {
        int ret = 0;

        int nNum = 0;

        {
            nNum = m_pRefNum->sub( 1 );
            //LOG4( "connection %x sub ref = %d(%s:%d)", this, nNum, local_ip(), local_port() );
        }

        if( 0 == nNum )
        {
            xos_box::i_msg * pMsg = mgr::container()->box()->msg();
            // 关掉超额的连接。因为此时还没有设置模块的数据，所以不能走模块的释放流程
            if( !get_user_data() )
            {
                pMsg->set_void( 0, this );
                helper_main::get()->notify( pMsg, MAIN_RELEASE_CONNECTION );
            }
            // 模块释放流程
            // 先打到xos_http_cat中，释放完那里的connect,再打回xos_net中，释放这里的connect
            // 这样做是为了保证获得完成释放的时刻，这样程序退出才没有问题
            else
            {
                pMsg->set_void( 0, net_connect() );
                notify()->notify( pMsg, net::NET_RELEASE_CONNECT );
            }
        }

        return ret;
    }

    int connection::add_ref()
    {
        int ret = 0;

        m_pRefNum->add( 1 );
        //int nNum = m_pRefNum->add(1);
        //LOG4( "connection %x add ref = %d(%s:%d)", this, nNum, local_ip(), local_port() );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    xos_aio::i_connect * connection::create_accept_connect()
    {
        connection * pConnect = 0;
        {
            tcp::T * pTcp = 0;
            tcp::init( pTcp, pConnect );
            pConnect->m_pListenCon = this;
            pTcp->m_pListenTcp = m_pTcp;
            pConnect->set_tcp_data();
        }
        return pConnect;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    const char * connection::local_ip()
    {
        const char * pRet = 0;
        if( m_pTcp )
        {
            pRet = m_pTcp->m_local_ip.c_str();
        }
        else
        {
            pRet = m_pUdp->m_local_ip.c_str();
        }
        return pRet;
    }

    int connection::local_port()
    {
        int nRet = 0;
        if( m_pTcp )
        {
            nRet = m_pTcp->m_nLocalPort;
        }
        else
        {
            nRet = m_pUdp->m_nLocalPort;
        }
        return nRet;
    }

    // 
    // peer ip and port
    // 
    const char * connection::peer_ip()
    {
        const char * pRet = 0;
        if( m_pTcp )
        {
            pRet = m_pTcp->m_peer_ip.c_str();
        }
        else
        {
            pRet = m_pUdp->m_peer_ip.c_str();
        }
        return pRet;
    }

    int connection::peer_port()
    {
        int nRet = 0;
        if( m_pTcp )
        {
            nRet = m_pTcp->m_nPeerPort;
        }
        else
        {
            nRet = m_pUdp->m_nPeerPort;
        }
        return nRet;
    }

    bool connection::is_tcp()
    {
        bool bRet = !is_udp();
        return bRet;
    }

} // xos_net
